import {
  Injectable,
  BadRequestException,
  NotFoundException,
} from '@nestjs/common';
import { PrismaService } from '../../common/prisma/prisma.service';
import { CreateKnowledgePointDto } from './dto/create-knowledge-point.dto';
import { KnowledgePointQueryDto } from './dto/knowledge-point-query.dto';
import { UpdateKnowledgePointDto } from './dto/update-knowledge-point.dto';
import {
  CategoryPath,
  KnowledgePoint,
  KnowledgePointListResponse,
} from './interfaces/knowledge-point.interface';
import { Prisma } from '@prisma/client';

type LevelNumber = 1 | 2 | 3 | 4 | 5 | 6;
type LevelTable = `level_${LevelNumber}`;

// 定义一个泛型类型，用于表示不同层级的字段结构
type LevelFields<T extends string> = {
  [K in `${T}_id` | `${T}_code` | `${T}_name`]: K extends `${T}_id`
    ? bigint
    : string;
};

type LevelDataMap = {
  [K in LevelTable]: LevelFields<K>;
};

type CategoryIdMap = {
  [K in LevelNumber as `level${K}Id`]: string | undefined;
};

type PrismaTableType = {
  [K in LevelTable]: {
    findFirst: (args: { where: Record<string, unknown> }) => Promise<any>;
  };
};

type PrismaQueryResult = Prisma.knowledge_pointGetPayload<any>;

@Injectable()
export class KnowledgePointService {
  private readonly levelSelects = {
    level_1: {
      select: { level_1_id: true, level_1_code: true, level_1_name: true },
    },
    level_2: {
      select: { level_2_id: true, level_2_code: true, level_2_name: true },
    },
    level_3: {
      select: { level_3_id: true, level_3_code: true, level_3_name: true },
    },
    level_4: {
      select: { level_4_id: true, level_4_code: true, level_4_name: true },
    },
    level_5: {
      select: { level_5_id: true, level_5_code: true, level_5_name: true },
    },
    level_6: {
      select: { level_6_id: true, level_6_code: true, level_6_name: true },
    },
  } as const;

  constructor(private prisma: PrismaService) {}

  private async validateCategoryHierarchy(
    categoryIds: CategoryIdMap,
  ): Promise<void> {
    // 检查是否有跳级的情况
    let previousLevel: number | null = null;
    for (let i = 1; i <= 6; i++) {
      const currentId = categoryIds[`level${i}Id` as keyof CategoryIdMap];
      if (currentId) {
        if (previousLevel === null) {
          previousLevel = i;
        } else if (i - previousLevel > 1) {
          throw new BadRequestException(`分类层级不连续，缺少第${i - 1}级分类`);
        }
        previousLevel = i;
      }
    }

    const validationPromises: Promise<void>[] = [];
    if (categoryIds.level1Id) {
      validationPromises.push(this.validateCategory(1, categoryIds.level1Id));
    }

    for (let i = 2; i <= 6; i++) {
      const currentId = categoryIds[`level${i}Id` as keyof CategoryIdMap];
      const parentId = categoryIds[`level${i - 1}Id` as keyof CategoryIdMap];
      if (currentId) {
        if (!parentId) {
          throw new BadRequestException(`第${i}级分类缺少上级分类`);
        }
        validationPromises.push(
          this.validateCategory(i as LevelNumber, currentId, parentId),
        );
      }
    }

    await Promise.all(validationPromises);
  }

  private async validateCategory(
    level: LevelNumber,
    categoryId: string,
    parentId?: string,
  ): Promise<void> {
    const tableName = `level_${level}` as LevelTable;
    const idField = `${tableName}_id`;
    const parentField = level > 1 ? `level_${level - 1}_id` : undefined;

    try {
      const where: Record<string, unknown> = {
        [idField]: BigInt(categoryId),
        is_deleted: 0,
      };

      if (parentId && parentField) {
        where[parentField] = BigInt(parentId);
      }

      const table = this.prisma[
        tableName
      ] as unknown as PrismaTableType[typeof tableName];
      const exists = await table.findFirst({ where });

      if (!exists) {
        throw new BadRequestException(`${level}级分类验证失败`);
      }
    } catch (error) {
      if (error instanceof BadRequestException) {
        throw error;
      }
      if (error instanceof Error) {
        throw new BadRequestException(`分类验证失败: ${error.message}`);
      }
      throw new BadRequestException('分类验证失败');
    }
  }

  private async findKnowledgePoint(id: bigint): Promise<PrismaQueryResult> {
    const result = await this.prisma.knowledge_point.findFirst({
      where: { knowledge_point_id: id, is_deleted: 0 },
    });

    if (!result) {
      throw new NotFoundException('知识点不存在');
    }

    return result;
  }

  private async checkCodeExists(
    code: string,
    excludeId?: bigint,
  ): Promise<boolean> {
    const where: Prisma.knowledge_pointWhereInput = {
      knowledge_point_code: code,
      is_deleted: 0,
    };

    if (excludeId) {
      where.knowledge_point_id = { not: excludeId };
    }

    const count = await this.prisma.knowledge_point.count({ where });
    return count > 0;
  }

  private formatKnowledgePoint(data: PrismaQueryResult): KnowledgePoint {
    const levels: LevelNumber[] = [1, 2, 3, 4, 5, 6];

    for (const level of levels) {
      const levelKey = `level_${level}` as LevelTable;
      const levelData = data[levelKey] as
        | LevelDataMap[typeof levelKey]
        | null
        | undefined;
    }

    return {
      id: data.knowledge_point_id.toString(),
      code: data.knowledge_point_code,
      description: data.knowledge_point_desc,
      example: data.example,
      level1Id: data.level_1_id?.toString(),
      level2Id: data.level_2_id?.toString(),
      level3Id: data.level_3_id?.toString(),
      level4Id: data.level_4_id?.toString(),
      level5Id: data.level_5_id?.toString(),
      level6Id: data.level_6_id?.toString(),
      createdAt: data.create_time,
      updatedAt: data.update_time,
    };
  }

  async create(createDto: CreateKnowledgePointDto): Promise<KnowledgePoint> {
    try {
      const exists = await this.checkCodeExists(createDto.knowledge_point_code);
      if (exists) {
        throw new BadRequestException('知识点编码已存在');
      }

      await this.validateCategoryHierarchy({
        level1Id: createDto.level1Id,
        level2Id: createDto.level2Id,
        level3Id: createDto.level3Id,
        level4Id: createDto.level4Id,
        level5Id: createDto.level5Id,
        level6Id: createDto.level6Id,
      });

      // 获取各级分类的名称
      const [level1, level2, level3, level4, level5, level6] =
        await Promise.all([
          createDto.level1Id
            ? this.prisma.level_1.findFirst({
                where: {
                  level_1_id: BigInt(createDto.level1Id),
                  is_deleted: 0,
                },
              })
            : null,
          createDto.level2Id
            ? this.prisma.level_2.findFirst({
                where: {
                  level_2_id: BigInt(createDto.level2Id),
                  is_deleted: 0,
                },
              })
            : null,
          createDto.level3Id
            ? this.prisma.level_3.findFirst({
                where: {
                  level_3_id: BigInt(createDto.level3Id),
                  is_deleted: 0,
                },
              })
            : null,
          createDto.level4Id
            ? this.prisma.level_4.findFirst({
                where: {
                  level_4_id: BigInt(createDto.level4Id),
                  is_deleted: 0,
                },
              })
            : null,
          createDto.level5Id
            ? this.prisma.level_5.findFirst({
                where: {
                  level_5_id: BigInt(createDto.level5Id),
                  is_deleted: 0,
                },
              })
            : null,
          createDto.level6Id
            ? this.prisma.level_6.findFirst({
                where: {
                  level_6_id: BigInt(createDto.level6Id),
                  is_deleted: 0,
                },
              })
            : null,
        ]);

      const data: Prisma.knowledge_pointCreateInput = {
        knowledge_point_code: createDto.knowledge_point_code,
        knowledge_point_desc: createDto.knowledge_point_desc,
        example: createDto.example || '',
        level_1_id: createDto.level1Id ? Number(createDto.level1Id) : null,
        level_1_name: level1?.level_1_name || null,
        level_2_id: createDto.level2Id ? Number(createDto.level2Id) : null,
        level_2_name: level2?.level_2_name || null,
        level_3_id: createDto.level3Id ? Number(createDto.level3Id) : null,
        level_3_name: level3?.level_3_name || null,
        level_4_id: createDto.level4Id ? Number(createDto.level4Id) : null,
        level_4_name: level4?.level_4_name || null,
        level_5_id: createDto.level5Id ? Number(createDto.level5Id) : null,
        level_5_name: level5?.level_5_name || null,
        level_6_id: createDto.level6Id ? Number(createDto.level6Id) : null,
        level_6_name: level6?.level_6_name || null,
        create_time: new Date(),
        update_time: new Date(),
        is_deleted: 0,
      };

      const result = await this.prisma.knowledge_point.create({
        data,
      });

      return {
        id: result.knowledge_point_id.toString(),
        code: result.knowledge_point_code,
        description: result.knowledge_point_desc,
        example: result.example,
        level1Id: result.level_1_id?.toString(),
        level2Id: result.level_2_id?.toString(),
        level3Id: result.level_3_id?.toString(),
        level4Id: result.level_4_id?.toString(),
        level5Id: result.level_5_id?.toString(),
        level6Id: result.level_6_id?.toString(),
        createdAt: result.create_time,
        updatedAt: result.update_time,
      };
    } catch (error) {
      if (error instanceof BadRequestException) {
        throw error;
      }
      if (error instanceof Error) {
        throw new BadRequestException(`创建知识点失败: ${error.message}`);
      }
      throw new BadRequestException('创建知识点失败');
    }
  }

  async update(
    id: bigint,
    updateDto: UpdateKnowledgePointDto,
  ): Promise<KnowledgePoint> {
    try {
      const exists = await this.findKnowledgePoint(id);

      if (updateDto.knowledge_point_code) {
        const codeExists = await this.checkCodeExists(
          updateDto.knowledge_point_code,
          id,
        );
        if (codeExists) {
          throw new BadRequestException('知识点编码已存在');
        }
      }

      if (Object.keys(updateDto).some((key) => key.startsWith('level'))) {
        await this.validateCategoryHierarchy({
          level1Id: updateDto.level1Id ?? exists.level_1_id?.toString(),
          level2Id: updateDto.level2Id ?? exists.level_2_id?.toString(),
          level3Id: updateDto.level3Id ?? exists.level_3_id?.toString(),
          level4Id: updateDto.level4Id ?? exists.level_4_id?.toString(),
          level5Id: updateDto.level5Id ?? exists.level_5_id?.toString(),
          level6Id: updateDto.level6Id ?? exists.level_6_id?.toString(),
        });
      }

      // 获取各级分类的名称
      const [level1, level2, level3, level4, level5, level6] =
        await Promise.all([
          updateDto.level1Id
            ? this.prisma.level_1.findFirst({
                where: {
                  level_1_id: BigInt(updateDto.level1Id),
                  is_deleted: 0,
                },
              })
            : null,
          updateDto.level2Id
            ? this.prisma.level_2.findFirst({
                where: {
                  level_2_id: BigInt(updateDto.level2Id),
                  is_deleted: 0,
                },
              })
            : null,
          updateDto.level3Id
            ? this.prisma.level_3.findFirst({
                where: {
                  level_3_id: BigInt(updateDto.level3Id),
                  is_deleted: 0,
                },
              })
            : null,
          updateDto.level4Id
            ? this.prisma.level_4.findFirst({
                where: {
                  level_4_id: BigInt(updateDto.level4Id),
                  is_deleted: 0,
                },
              })
            : null,
          updateDto.level5Id
            ? this.prisma.level_5.findFirst({
                where: {
                  level_5_id: BigInt(updateDto.level5Id),
                  is_deleted: 0,
                },
              })
            : null,
          updateDto.level6Id
            ? this.prisma.level_6.findFirst({
                where: {
                  level_6_id: BigInt(updateDto.level6Id),
                  is_deleted: 0,
                },
              })
            : null,
        ]);

      const data: Prisma.knowledge_pointUpdateInput = {
        ...(updateDto.knowledge_point_code !== undefined && {
          knowledge_point_code: updateDto.knowledge_point_code,
        }),
        ...(updateDto.knowledge_point_desc !== undefined && {
          knowledge_point_desc: updateDto.knowledge_point_desc,
        }),
        ...(updateDto.example !== undefined && {
          example: updateDto.example,
        }),
        ...(updateDto.level1Id !== undefined && {
          level_1_id: updateDto.level1Id ? Number(updateDto.level1Id) : null,
          level_1_name: level1?.level_1_name || null,
        }),
        ...(updateDto.level2Id !== undefined && {
          level_2_id: updateDto.level2Id ? Number(updateDto.level2Id) : null,
          level_2_name: level2?.level_2_name || null,
        }),
        ...(updateDto.level3Id !== undefined && {
          level_3_id: updateDto.level3Id ? Number(updateDto.level3Id) : null,
          level_3_name: level3?.level_3_name || null,
        }),
        ...(updateDto.level4Id !== undefined && {
          level_4_id: updateDto.level4Id ? Number(updateDto.level4Id) : null,
          level_4_name: level4?.level_4_name || null,
        }),
        ...(updateDto.level5Id !== undefined && {
          level_5_id: updateDto.level5Id ? Number(updateDto.level5Id) : null,
          level_5_name: level5?.level_5_name || null,
        }),
        ...(updateDto.level6Id !== undefined && {
          level_6_id: updateDto.level6Id ? Number(updateDto.level6Id) : null,
          level_6_name: level6?.level_6_name || null,
        }),
        update_time: new Date(),
      };

      const result = await this.prisma.knowledge_point.update({
        where: { knowledge_point_id: id },
        data,
      });

      return this.formatKnowledgePoint(result);
    } catch (error) {
      if (
        error instanceof BadRequestException ||
        error instanceof NotFoundException
      ) {
        throw error;
      }
      throw new BadRequestException(`更新知识点失败: ${error.message}`);
    }
  }

  async delete(id: bigint): Promise<KnowledgePoint> {
    try {
      // 先检查知识点是否存在
      await this.findKnowledgePoint(id);

      const result = await this.prisma.knowledge_point.update({
        where: { knowledge_point_id: id },
        data: { is_deleted: 1, update_time: new Date() },
      });

      return {
        id: result.knowledge_point_id.toString(),
        code: result.knowledge_point_code,
        description: result.knowledge_point_desc,
        example: result.example,
        level1Id: result.level_1_id?.toString(),
        level2Id: result.level_2_id?.toString(),
        level3Id: result.level_3_id?.toString(),
        level4Id: result.level_4_id?.toString(),
        level5Id: result.level_5_id?.toString(),
        level6Id: result.level_6_id?.toString(),
        createdAt: result.create_time,
        updatedAt: result.update_time,
      };
    } catch (error) {
      if (error instanceof NotFoundException) {
        throw error;
      }
      throw new BadRequestException(`删除知识点失败: ${error.message}`);
    }
  }

  async batchDelete(ids: bigint[]): Promise<void> {
    try {
      const result = await this.prisma.knowledge_point.updateMany({
        where: { knowledge_point_id: { in: ids }, is_deleted: 0 },
        data: { is_deleted: 1, update_time: new Date() },
      });

      if (result.count === 0) {
        throw new BadRequestException('没有找到可删除的知识点');
      }
    } catch (error) {
      if (error instanceof BadRequestException) {
        throw error;
      }
      throw new BadRequestException(`批量删除知识点失败: ${error.message}`);
    }
  }

  async findAll(
    query: KnowledgePointQueryDto,
  ): Promise<KnowledgePointListResponse> {
    try {
      const { page, size, level1Id, keyword, startTime, endTime } = query;
      const where: Prisma.knowledge_pointWhereInput = { is_deleted: 0 };

      if (level1Id) {
        where.level_1_id = Number(level1Id);
      }

      if (keyword) {
        where.OR = [
          { knowledge_point_code: { contains: keyword } },
          { knowledge_point_desc: { contains: keyword } },
          { example: { contains: keyword } },
        ];
      }

      if (startTime && endTime) {
        where.create_time = { gte: startTime, lte: endTime };
      }

      const skip = (Number(page) - 1) * Number(size);
      const take = Number(size);

      const [total, items] = await Promise.all([
        this.prisma.knowledge_point.count({ where }),
        this.prisma.knowledge_point.findMany({
          where,
          skip,
          take,
          orderBy: { create_time: 'desc' },
        }),
      ]);

      return {
        items: items.map((item) => {
          return {
            id: item.knowledge_point_id.toString(),
            code: item.knowledge_point_code,
            description: item.knowledge_point_desc,
            example: item.example,
            level1Id: item.level_1_id?.toString(),
            level2Id: item.level_2_id?.toString(),
            level3Id: item.level_3_id?.toString(),
            level4Id: item.level_4_id?.toString(),
            level5Id: item.level_5_id?.toString(),
            level6Id: item.level_6_id?.toString(),
            createdAt: item.create_time,
            updatedAt: item.update_time,
          };
        }),
        total,
        page: Number(page),
        size: Number(size),
        totalPages: Math.ceil(total / Number(size)),
      };
    } catch (error) {
      throw new BadRequestException(`查询知识点列表失败: ${error.message}`);
    }
  }

  async findOne(id: bigint): Promise<KnowledgePoint> {
    try {
      const result = await this.findKnowledgePoint(id);
      return this.formatKnowledgePoint(result);
    } catch (error) {
      if (error instanceof NotFoundException) {
        throw error;
      }
      throw new BadRequestException(`查询知识点失败: ${error.message}`);
    }
  }
}
